﻿using MathNet.Numerics.Distributions;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace HARTCalibrationTool.Commands.TwoDimensionalCMDGroup
{
    public class CMDsGroupManager
    {
        private Queue<(CMDsGroup, TimeSpan)> _commandGroupQueue = new Queue<(CMDsGroup, TimeSpan)>();
        private SemaphoreSlim _pauseSemaphore = new SemaphoreSlim(1, 1);
        private CancellationTokenSource _cts = new CancellationTokenSource();
        private bool _isStopped = false;

        // 每一路可以有多个回调函数，因此使用List<List<Delegate>>
        private static List<(Delegate, object[])>[] callbackGroups = new List<(Delegate, object[])>[32];

        private static List<(Delegate, object[])> callbackGroupsEnd = new List<(Delegate, object[])>();

        static CMDsGroupManager()
        {
            for (byte i = 0; i < 32; i++)
            {
                callbackGroups[i] = new List<(Delegate, object[])>();
            }
        }

        public CMDsGroupManager(IEnumerable<(CMDsGroup, TimeSpan)> commandGroups)
        { 
            foreach (var group in commandGroups)
            {
                _commandGroupQueue.Enqueue(group);
            }
        }

        public async Task ExecuteAllGroupsAsync(bool isStopAfTimeout = true)
        {
            bool isSuccess = false;
            bool isExed = false;

            while (_commandGroupQueue.Count > 0 && !_cts.Token.IsCancellationRequested)
            {
                isExed = true;

                var (currentGroup, delay) = _commandGroupQueue.Peek();

                try
                {
                    isSuccess = await currentGroup.ExecuteGroupAsync(_cts.Token, _isStopped, isStopAfTimeout);

                    _commandGroupQueue.Dequeue();

                    if(isSuccess)
                    {
                        // callback
                        int slot = (int)(currentGroup.CurrentGroupChl - 1);

                        if ((slot >= 0) && (slot <= 31))
                        {
                            List<(Delegate, object[])> callbacks = callbackGroups[slot];

                            if (callbacks?.Count > 0)
                            {
                                TriggerGroupsCallbacks((int)currentGroup.CurrentGroupChl);
                            }
                        }

                        if (_commandGroupQueue.Count != 0)
                        {
                            await Task.Delay(delay, _cts.Token);
                        }
                    }
                }
                catch (OperationCanceledException ex)
                {
                    MessageBox.Show(ex.Message, "错误", buttons:MessageBoxButtons.OK, icon:MessageBoxIcon.Error);
                }

                if (!_cts.Token.IsCancellationRequested && _isStopped)
                {
                    _commandGroupQueue.Dequeue();
                }
                else if (_isStopped)
                {
                    break;
                }
            }

            if (isExed)
            {
                TriggerGroupsEndCallbacks();
            }

            CommandsBase.bHasCMDExecuting = false;
        }

        public void Pause()
        {
            _isStopped = true;
        }

        public void Resume()
        {
            _isStopped = false;
            Task.Run(() => ExecuteAllGroupsAsync());
        }

        public void Stop()
        {
            _cts.Cancel();
        }

        public void EnqueueGroup(CMDsGroup group, TimeSpan delay)
        {
            _commandGroupQueue.Enqueue((group, delay));
        }

        public static void AddGroupsCallback(int chl, Action<object[]> callback, params object[] args)
        {
            int slot = chl - 1;

            if (slot < 0 || slot > 31)
            {
                return;
            }

            callbackGroups[slot].Add((callback, args));
        }

        public static void TriggerGroupsCallbacks(int chl)
        {
            int slot = chl - 1;

            if (slot < 0 || slot > 31)
            {
                return;
            }

            List<(Delegate, object[])> callbacks = callbackGroups[slot];

            foreach (var (callback, args) in callbacks)
            {
                if (callback is Action<object[]> typedCallback)
                {
                    typedCallback(args);
                }
            }

            callbacks.Clear();
        }

        public static void AddGroupsEndCallback(Action<object[]> callback, params object[] args)
        {
            callbackGroupsEnd.Add((callback, args));
        }

        public static void TriggerGroupsEndCallbacks()
        {
            foreach (var (callback, args) in callbackGroupsEnd)
            {
                if (callback is Action<object[]> typedCallback)
                {
                    typedCallback(args);
                }
            }

            callbackGroupsEnd.Clear();
        }
    }
}
